// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// A BrowserPluginGuest is the browser side of a browser <--> embedder
// renderer channel. A BrowserPlugin (a WebPlugin) is on the embedder
// renderer side of browser <--> embedder renderer communication.
//
// BrowserPluginGuest lives on the UI thread of the browser process. Any
// messages about the guest render process that the embedder might be interested
// in receiving should be listened for here.
//
// BrowserPluginGuest is a WebContentsObserver for the guest WebContents.
// BrowserPluginGuest operates under the assumption that the guest will be
// accessible through only one RenderViewHost for the lifetime of
// the guest WebContents. Thus, cross-process navigation is not supported.

#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_H_
#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_H_

#include <stdint.h>

#include <map>
#include <memory>
#include <queue>

#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "build/build_config.h"
#include "content/common/edit_command.h"
#include "content/common/input/input_event_ack_state.h"
#include "content/public/browser/browser_plugin_guest_delegate.h"
#include "content/public/browser/guest_host.h"
#include "content/public/browser/readback_types.h"
#include "content/public/browser/web_contents_observer.h"
#include "third_party/WebKit/public/platform/WebDragOperation.h"
#include "third_party/WebKit/public/platform/WebFocusType.h"
#include "third_party/WebKit/public/platform/WebInputEvent.h"
#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
#include "third_party/WebKit/public/web/WebDragStatus.h"
#include "ui/base/ime/text_input_mode.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/gfx/geometry/rect.h"

struct BrowserPluginHostMsg_Attach_Params;

#if defined(OS_MACOSX)
struct FrameHostMsg_ShowPopup_Params;
#endif

namespace cc {
class SurfaceId;
struct SurfaceSequence;
} // namespace cc

namespace gfx {
class Range;
} // namespace gfx

namespace content {

class BrowserPluginGuestManager;
class RenderViewHostImpl;
class RenderWidgetHost;
class RenderWidgetHostImpl;
class RenderWidgetHostView;
class RenderWidgetHostViewBase;
class SiteInstance;
struct DropData;
struct TextInputState;

// A browser plugin guest provides functionality for WebContents to operate in
// the guest role and implements guest-specific overrides for ViewHostMsg_*
// messages.
//
// When a guest is initially created, it is in an unattached state. That is,
// it is not visible anywhere and has no embedder WebContents assigned.
// A BrowserPluginGuest is said to be "attached" if it has an embedder.
// A BrowserPluginGuest can also create a new unattached guest via
// CreateNewWindow. The newly created guest will live in the same partition,
// which means it can share storage and can script this guest.
//
// Note: in --site-per-process, all IPCs sent out from this class will be
// dropped on the floor since we don't have a BrowserPlugin.
class CONTENT_EXPORT BrowserPluginGuest : public GuestHost,
                                          public WebContentsObserver {
public:
    ~BrowserPluginGuest() override;

    // The WebContents passed into the factory method here has not been
    // initialized yet and so it does not yet hold a SiteInstance.
    // BrowserPluginGuest must be constructed and installed into a WebContents
    // prior to its initialization because WebContents needs to determine what
    // type of WebContentsView to construct on initialization. The content
    // embedder needs to be aware of |guest_site_instance| on the guest's
    // construction and so we pass it in here.
    static BrowserPluginGuest* Create(WebContentsImpl* web_contents,
        BrowserPluginGuestDelegate* delegate);

    // Returns whether the given WebContents is a BrowserPlugin guest.
    static bool IsGuest(WebContentsImpl* web_contents);

    // Returns whether the given RenderviewHost is a BrowserPlugin guest.
    static bool IsGuest(RenderViewHostImpl* render_view_host);

    // BrowserPluginGuest::Init is called after the associated guest WebContents
    // initializes. If this guest cannot navigate without being attached to a
    // container, then this call is a no-op. For guest types that can be
    // navigated, this call adds the associated RenderWdigetHostViewGuest to the
    // view hierachy and sets up the appropriate RendererPreferences so that this
    // guest can navigate and resize offscreen.
    void Init();

    // Returns a WeakPtr to this BrowserPluginGuest.
    base::WeakPtr<BrowserPluginGuest> AsWeakPtr();

    // Sets the focus state of the current RenderWidgetHostView.
    void SetFocus(RenderWidgetHost* rwh,
        bool focused,
        blink::WebFocusType focus_type);

    // Sets the tooltip text.
    void SetTooltipText(const base::string16& tooltip_text);

    // Sets the lock state of the pointer. Returns true if |allowed| is true and
    // the mouse has been successfully locked.
    bool LockMouse(bool allowed);

    // Return true if the mouse is locked.
    bool mouse_locked() const { return mouse_locked_; }

    // Called when the embedder WebContents changes visibility.
    void EmbedderVisibilityChanged(bool visible);

    // Creates a new guest WebContentsImpl with the provided |params| with |this|
    // as the |opener|.
    WebContentsImpl* CreateNewGuestWindow(
        const WebContents::CreateParams& params);

    // Creates, if necessary, and returns the routing ID of a proxy for the guest
    // in the owner's renderer process.
    int GetGuestProxyRoutingID();

    // Returns the identifier that uniquely identifies a browser plugin guest
    // within an embedder.
    int browser_plugin_instance_id() const { return browser_plugin_instance_id_; }

    bool OnMessageReceivedFromEmbedder(const IPC::Message& message);

    WebContentsImpl* embedder_web_contents() const
    {
        return attached_ ? owner_web_contents_ : nullptr;
    }

    // Returns the embedder's RenderWidgetHostView if it is available.
    // Returns nullptr otherwise.
    RenderWidgetHostView* GetOwnerRenderWidgetHostView();

    bool focused() const { return focused_; }
    bool visible() const { return guest_visible_; }
    bool is_in_destruction() { return is_in_destruction_; }

    void UpdateVisibility();

    BrowserPluginGuestManager* GetBrowserPluginGuestManager() const;

    // WebContentsObserver implementation.
    void DidCommitProvisionalLoadForFrame(
        RenderFrameHost* render_frame_host,
        const GURL& url,
        ui::PageTransition transition_type) override;

    void RenderViewReady() override;
    void RenderProcessGone(base::TerminationStatus status) override;
    bool OnMessageReceived(const IPC::Message& message) override;
    bool OnMessageReceived(const IPC::Message& message,
        RenderFrameHost* render_frame_host) override;

    // GuestHost implementation.
    int LoadURLWithParams(
        const NavigationController::LoadURLParams& load_params) override;
    void GuestResizeDueToAutoResize(const gfx::Size& new_size) override;
    void SizeContents(const gfx::Size& new_size) override;
    void WillDestroy() override;

    // Exposes the protected web_contents() from WebContentsObserver.
    WebContentsImpl* GetWebContents() const;

    gfx::Point GetScreenCoordinates(const gfx::Point& relative_position) const;

    // This method is called by the RenderWidgetHostViewGuest to inform the
    // BrowserPlugin of the potential location of the context menu event (to
    // come). The need for this (hack) is that the input events when passed on to
    // the BrowserPlugin are modified by any CSS transforms applied on the plugin.
    // Therefore, the coordinates of the context menu event with respect to the
    // container window are modifed with the guest renderer process beiung unaware
    // of the change. Then eventually, when the context menu event arrives at the
    // browser, it contains the wrong coordinates (BUG=470087).
    // TODO(ekaramad): Find a more fundamental solution and remove this later.
    void SetContextMenuPosition(const gfx::Point& position);

    // Helper to send messages to embedder. If this guest is not yet attached,
    // then IPCs will be queued until attachment.
    void SendMessageToEmbedder(std::unique_ptr<IPC::Message> msg);

    // Returns whether the guest is attached to an embedder.
    bool attached() const { return attached_; }

    // Returns true when an attachment has taken place since the last time the
    // compositor surface was set.
    bool has_attached_since_surface_set() const
    {
        return has_attached_since_surface_set_;
    }

    // Attaches this BrowserPluginGuest to the provided |embedder_web_contents|
    // and initializes the guest with the provided |params|. Attaching a guest
    // to an embedder implies that this guest's lifetime is no longer managed
    // by its opener, and it can begin loading resources.
    void Attach(int browser_plugin_instance_id,
        WebContentsImpl* embedder_web_contents,
        const BrowserPluginHostMsg_Attach_Params& params);

    // Returns whether BrowserPluginGuest is interested in receiving the given
    // |message|.
    static bool ShouldForwardToBrowserPluginGuest(const IPC::Message& message);

    void DragSourceEndedAt(int client_x, int client_y, int screen_x,
        int screen_y, blink::WebDragOperation operation);

    // Called when the drag started by this guest ends at an OS-level.
    void EmbedderSystemDragEnded();
    void EndSystemDragIfApplicable();

    void RespondToPermissionRequest(int request_id,
        bool should_allow,
        const std::string& user_input);

    void PointerLockPermissionResponse(bool allow);

    // The next function is virtual for test purposes.
    virtual void SetChildFrameSurface(const cc::SurfaceId& surface_id,
        const gfx::Size& frame_size,
        float scale_factor,
        const cc::SurfaceSequence& sequence);

    // Find the given |search_text| in the page. Returns true if the find request
    // is handled by this browser plugin guest.
    bool HandleFindForEmbedder(int request_id,
        const base::string16& search_text,
        const blink::WebFindOptions& options);
    bool HandleStopFindingForEmbedder(StopFindAction action);

    void ResendEventToEmbedder(const blink::WebInputEvent& event);

    // TODO(ekaramad): Remove this once https://crbug.com/642826 is resolved.
    bool can_use_cross_process_frames() const
    {
        return can_use_cross_process_frames_;
    }

    gfx::Point GetCoordinatesInEmbedderWebContents(
        const gfx::Point& relative_point);

protected:
    // BrowserPluginGuest is a WebContentsObserver of |web_contents| and
    // |web_contents| has to stay valid for the lifetime of BrowserPluginGuest.
    // Constructor protected for testing.
    BrowserPluginGuest(bool has_render_view,
        WebContentsImpl* web_contents,
        BrowserPluginGuestDelegate* delegate);

    // Protected for testing.
    void set_has_attached_since_surface_set_for_test(bool has_attached)
    {
        has_attached_since_surface_set_ = has_attached;
    }

    void set_attached_for_test(bool attached)
    {
        attached_ = attached;
    }

private:
    class EmbedderVisibilityObserver;

    // The RenderWidgetHostImpl corresponding to the owner frame of BrowserPlugin.
    RenderWidgetHostImpl* GetOwnerRenderWidgetHost() const;

    void InitInternal(const BrowserPluginHostMsg_Attach_Params& params,
        WebContentsImpl* owner_web_contents);

    void OnSatisfySequence(int instance_id, const cc::SurfaceSequence& sequence);
    void OnRequireSequence(int instance_id,
        const cc::SurfaceId& id,
        const cc::SurfaceSequence& sequence);
    // Message handlers for messages from embedder.
    void OnDetach(int instance_id);
    // Handles drag events from the embedder.
    // When dragging, the drag events go to the embedder first, and if the drag
    // happens on the browser plugin, then the plugin sends a corresponding
    // drag-message to the guest. This routes the drag-message to the guest
    // renderer.
    void OnDragStatusUpdate(int instance_id,
        blink::WebDragStatus drag_status,
        const DropData& drop_data,
        blink::WebDragOperationsMask drag_mask,
        const gfx::Point& location);
    // Instructs the guest to execute an edit command decoded in the embedder.
    void OnExecuteEditCommand(int instance_id,
        const std::string& command);

    void OnLockMouse(bool user_gesture,
        bool last_unlocked_by_target,
        bool privileged);
    void OnLockMouseAck(int instance_id, bool succeeded);
    // Resizes the guest's web contents.
    void OnSetFocus(int instance_id,
        bool focused,
        blink::WebFocusType focus_type);
    // Sets the name of the guest so that other guests in the same partition can
    // access it.
    void OnSetName(int instance_id, const std::string& name);
    // Updates the size state of the guest.
    void OnSetEditCommandsForNextKeyEvent(
        int instance_id,
        const std::vector<EditCommand>& edit_commands);
    // The guest WebContents is visible if both its embedder is visible and
    // the browser plugin element is visible. If either one is not then the
    // WebContents is marked as hidden. A hidden WebContents will consume
    // fewer GPU and CPU resources.
    //
    // When every WebContents in a RenderProcessHost is hidden, it will lower
    // the priority of the process (see RenderProcessHostImpl::WidgetHidden).
    //
    // It will also send a message to the guest renderer process to cleanup
    // resources such as dropping back buffers and adjusting memory limits (if in
    // compositing mode, see CCLayerTreeHost::setVisible).
    //
    // Additionally, it will slow down Javascript execution and garbage
    // collection. See RenderThreadImpl::IdleHandler (executed when hidden) and
    // RenderThreadImpl::IdleHandlerInForegroundTab (executed when visible).
    void OnSetVisibility(int instance_id, bool visible);
    void OnUnlockMouse();
    void OnUnlockMouseAck(int instance_id);
    void OnUpdateGeometry(int instance_id, const gfx::Rect& view_rect);

    void OnTextInputStateChanged(const TextInputState& params);
    void OnImeSetComposition(
        int instance_id,
        const std::string& text,
        const std::vector<blink::WebCompositionUnderline>& underlines,
        int selection_start,
        int selection_end);
    void OnImeCommitText(
        int instance_id,
        const std::string& text,
        const std::vector<blink::WebCompositionUnderline>& underlines,
        int relative_cursor_pos);
    void OnImeFinishComposingText(bool keep_selection);
    void OnExtendSelectionAndDelete(int instance_id, int before, int after);
    void OnImeCancelComposition();
#if defined(OS_MACOSX) || defined(USE_AURA)
    void OnImeCompositionRangeChanged(
        const gfx::Range& range,
        const std::vector<gfx::Rect>& character_bounds);
#endif

    // Message handlers for messages from guest.
    void OnHandleInputEventAck(
        blink::WebInputEvent::Type event_type,
        InputEventAckState ack_result);
    void OnHasTouchEventHandlers(bool accept);
#if defined(OS_MACOSX)
    // On MacOS X popups are painted by the browser process. We handle them here
    // so that they are positioned correctly.
    void OnShowPopup(RenderFrameHost* render_frame_host,
        const FrameHostMsg_ShowPopup_Params& params);
#endif
    void OnShowWidget(int route_id, const gfx::Rect& initial_rect);
    void OnTakeFocus(bool reverse);
    void OnUpdateFrameName(int frame_id,
        bool is_top_level,
        const std::string& name);

    // Called when WillAttach is complete.
    void OnWillAttachComplete(WebContentsImpl* embedder_web_contents,
        const BrowserPluginHostMsg_Attach_Params& params);

    // Returns identical message with current browser_plugin_instance_id() if
    // the input was created with browser_plugin::kInstanceIdNone, else it returns
    // the input message unmodified. If no current browser_plugin_instance_id()
    // is set, or anything goes wrong, the input message is returned.
    std::unique_ptr<IPC::Message> UpdateInstanceIdIfNecessary(
        std::unique_ptr<IPC::Message> msg) const;

    // Forwards all messages from the |pending_messages_| queue to the embedder.
    void SendQueuedMessages();

    void SendTextInputTypeChangedToView(RenderWidgetHostViewBase* guest_rwhv);

    // The last tooltip that was set with SetTooltipText().
    base::string16 current_tooltip_text_;

    std::unique_ptr<EmbedderVisibilityObserver> embedder_visibility_observer_;
    WebContentsImpl* owner_web_contents_;

    // Indicates whether this guest has been attached to a container.
    bool attached_;

    // Used to signal if a browser plugin has been attached since the last time
    // the compositing surface was set.
    bool has_attached_since_surface_set_;

    // An identifier that uniquely identifies a browser plugin within an embedder.
    int browser_plugin_instance_id_;
    gfx::Rect guest_window_rect_;
    bool focused_;
    bool mouse_locked_;
    bool pending_lock_request_;
    bool guest_visible_;
    bool embedder_visible_;
    // Whether the browser plugin is inside a plugin document.
    bool is_full_page_plugin_;

    // Indicates that this BrowserPluginGuest has associated renderer-side state.
    // This is used to determine whether or not to create a new RenderView when
    // this guest is attached. A BrowserPluginGuest would have renderer-side state
    // prior to attachment if it is created via a call to window.open and
    // maintains a JavaScript reference to its opener.
    bool has_render_view_;

    // Last seen size of guest contents (by SwapCompositorFrame).
    gfx::Size last_seen_view_size_;

    bool is_in_destruction_;

    // BrowserPluginGuest::Init can only be called once. This flag allows it to
    // exit early if it's already been called.
    bool initialized_;

    // Text input type states.
    // Using scoped_ptr to avoid including the header file: view_messages.h.
    std::unique_ptr<const TextInputState> last_text_input_state_;

    // The is the routing ID for a swapped out RenderView for the guest
    // WebContents in the embedder's process.
    int guest_proxy_routing_id_;
    // Last seen state of drag status update.
    blink::WebDragStatus last_drag_status_;
    // Whether or not our embedder has seen a SystemDragEnded() call.
    bool seen_embedder_system_drag_ended_;
    // Whether or not our embedder has seen a DragSourceEndedAt() call.
    bool seen_embedder_drag_source_ended_at_;

    // Ignore the URL dragged into guest that is coming from guest.
    bool ignore_dragged_url_;

    // This is a queue of messages that are destined to be sent to the embedder
    // once the guest is attached to a particular embedder.
    std::deque<std::unique_ptr<IPC::Message>> pending_messages_;

    BrowserPluginGuestDelegate* const delegate_;

    // Whether or not this BrowserPluginGuest can use cross process frames. This
    // means when we have --use-cross-process-frames-for-guests on, the
    // WebContents associated with this BrowserPluginGuest has OOPIF structure.
    bool can_use_cross_process_frames_;

    // Weak pointer used to ask GeolocationPermissionContext about geolocation
    // permission.
    base::WeakPtrFactory<BrowserPluginGuest> weak_ptr_factory_;

    DISALLOW_COPY_AND_ASSIGN(BrowserPluginGuest);
};

} // namespace content

#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_H_
